package com.zhanglijie.base.volatileLearn;

import java.lang.reflect.Field;

import sun.misc.Unsafe;

/**
 * @author zhanglijie
 * @version 1.0
 * @since 1.1.0 2022/5/19 0019 0:05
 *  加上写屏障
 *
 *  storestore 写写屏障
 *  storeload  写读屏障（x86只有这个）
 *  loadload   读读屏障
 *  loadstore  读写屏障
 *
 *   lock前缀：
 *   volatile底层也是lock前缀
 *   lock前缀的作用：锁住cache和总线，强制刷storebuffer和cache，也就是串行化
 *  jdk中只提供了
 *     public native void loadFence(); （loadload屏障但是空操作，x86没有 invalid-queue）
 *
 *     public native void storeFence(); （storestore屏障也是空操作，因为x86规定所有写操作都进storebuffer这个队列排队fifo的）
 *
 *     public native void fullFence();  （storeload屏障这个真的有实现,加锁前缀）
 *
 *     不提供loadstore屏障
 */
public class FenceExam {
    private static int x=0,y=0;
    private static int a=0,b=0;
    public static Unsafe getUnsafe(){
        Field field = null;
        try{
            field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            return (Unsafe) field.get(null);
        }catch (Exception e){
          e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args) throws InterruptedException {
        int i = 0;
       for(;;){
          i++;
          x = 0;
          y = 0;
          a = 0;
          b = 0;
           Thread t1 = new Thread(() -> {
               /**
                * 下面的程序完整是
                *  a = 1; //写操作
                *  int temp = b;  //读操作
                *  x = temp;
                *  所以是典型的写读重排(x86只有写读操作会发生重排），导致如下
                *  int temp = b;
                *  a = 1;
                *  x = temp ; (x赋值的是老b的值）
                */
               a = 1;
               //真正起作用的时反射时候 通过cas操作了 让他加了个lock前缀
               FenceExam.getUnsafe().storeFence();
               x= b;
           });
           Thread t2 = new Thread(() -> {
               b = 1;
               FenceExam.getUnsafe().storeFence();
               y = a;
           });
           t1.start();
           t2.start();
           t1.join();
           t2.join();
           /**
            * 出现的情况：如果t1先跑完 t2再跑          x=0,y=1
            *           如果t2先跑完 t1再跑          x=1,y=0
            *           如果t1和t2跑的过程中交替的跑   x=1,y=1
            *           但是对于  x=0,y=0的结果好像怎么排列组合t1 t2跑的顺序也无法得到，但是x=0,y=0这个真的会出现(第 25518次  x=0 && y=0)
            *           这个就是指令重排了
            *             t1: 指令重排为:  x=b;
            *                            a=1;  ====>重排的结果就是x=0 y=0
            *             t2  指令重排为： y=a;
            *                            b=1
            *
            */
           System.out.println("第 "+i +" 次 x= "+x+" y= "+y);
           if(x == 0 && y == 0){
               System.out.println("第 "+i+"次  x=0 && y=0");
               break;
           }
       }
    }
}
